package com.example.composeanimationdemo

import android.annotation.SuppressLint
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.animation.*
import androidx.compose.animation.core.*
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.material.Button
import androidx.compose.material.Icon
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Phone
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Color.Companion.Blue
import androidx.compose.ui.graphics.Color.Companion.Red
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.example.composeanimationdemo.ui.theme.ComposeAnimationDemoTheme

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            ComposeAnimationDemoTheme {
                //AnimateAsStateDemo()
                //AnimateVisibilityDemo()
                AnimateContentSizeDemo()
                //CrossFadeDemo()
                //UpdateTransitionDemo()
                //AnimationContentDemo()
            }
        }
    }

    @Preview
    @Composable
    fun AnimateAsStateDemo() {
        var blue by remember { mutableStateOf(true) }
        val color by animateColorAsState(
            if (blue) Blue else Red,
            animationSpec = spring(stiffness = Spring.StiffnessVeryLow),
            finishedListener = {
                blue = !blue
            }
        )
        Column(Modifier.padding(16.dp)) {
            Text("AnimateAsStateDemo")
            Spacer(Modifier.height(16.dp))
            Button(
                onClick = { blue = !blue }
            ) {
                Text("Change Color")
            }
            Spacer(Modifier.height(16.dp))
            Box(
                Modifier
                    .size(128.dp)
                    .background(color)
            )
        }
    }

    private sealed class BoxState(val color: Color, val size: Dp) {
        operator fun not() = if (this is Small) Large else Small

        object Small : BoxState(Blue, 64.dp)
        object Large : BoxState(Red, 128.dp)
    }

    @SuppressLint("UnusedTransitionTargetStateParameter")
    @Preview
    @Composable
    fun UpdateTransitionDemo() {
        var boxState: BoxState by remember { mutableStateOf(BoxState.Small) }
        val transition = updateTransition(targetState = boxState, label = "")
        Column(Modifier.padding(16.dp)) {
            Text("UpdateTransitionDemo")
            Spacer(Modifier.height(16.dp))
            val color by transition.animateColor(label = "") {
                boxState.color
            }
            val size by transition.animateDp(transitionSpec = {
                if (targetState == BoxState.Large) {
                    spring(stiffness = Spring.StiffnessVeryLow)
                } else {
                    spring(stiffness = Spring.StiffnessHigh)
                }
            }, label = "") {
                boxState.size
            }
            Button(
                onClick = { boxState = !boxState }
            ) {
                Text("Change Color and size")
            }
            Spacer(Modifier.height(16.dp))
            Box(
                Modifier
                    .size(size)
                    .background(color)
            )
        }
    }


    @OptIn(ExperimentalAnimationApi::class)
    @Preview
    @Composable
    fun AnimateVisibilityDemo() {
        var visible by remember { mutableStateOf(true) }
        val density = LocalDensity.current
        Column(Modifier.padding(16.dp)) {
            Text("AnimateVisibilityDemo")
            Spacer(Modifier.height(16.dp))
            Button(
                onClick = { visible = !visible }
            ) {
                Text(text = if (visible) "Hide" else "Show")
            }
            Spacer(Modifier.height(16.dp))
            AnimatedVisibility(visible,
                enter = slideInVertically {
                    // Slide in from 40 dp from the top.
                    with(density) { -40.dp.roundToPx() }
                } + expandVertically(
                    // Expand from the top.
                    expandFrom = Alignment.Top
                ) + fadeIn(
                    // Fade in with the initial alpha of 0.3f.
                    initialAlpha = 0.3f
                ),
                exit = slideOutVertically() + shrinkVertically() + fadeOut())
            {
                Box(
                    Modifier
                        .size(128.dp)
                        .background(Blue)
                        .animateEnterExit(
                            // Slide in/out the inner box.
                            enter = slideInVertically(),
                            exit = slideOutVertically()
                        )
                )
            }
        }
    }


    @Preview
    @Composable
    fun AnimateContentSizeDemo() {
        var expend by remember { mutableStateOf(false) }
        Column(Modifier.padding(16.dp)) {
            Text("AnimateContentSizeDemo")
            Spacer(Modifier.height(16.dp))
            Button(
                onClick = { expend = !expend }
            ) {
                Text(if (expend) "Shrink" else "Expand")
            }
            Spacer(Modifier.height(16.dp))
            Box(
                Modifier
                    .background(Color.LightGray)
                    .animateContentSize()
            ) {
                Text(
                    text = "animateContentSize() animates its own size when its child modifier (or the child composable if it is already at the tail of the chain) changes size. " +
                            "This allows the parent modifier to observe a smooth size change, resulting in an overall continuous visual change.",
                    fontSize = 16.sp,
                    textAlign = TextAlign.Justify,
                    modifier = Modifier.padding(16.dp),
                    maxLines = if (expend) Int.MAX_VALUE else 2
                )
            }
        }
    }

    private enum class DemoScene {
        Text, Icon
    }

    @SuppressLint("UnusedCrossFadeTargetStateParameter")
    @Preview
    @Composable
    fun CrossFadeDemo() {
        var scene by remember { mutableStateOf(DemoScene.Text) }
        Column(Modifier.padding(16.dp)) {
            Text("CrossFadeDemo")
            Spacer(Modifier.height(16.dp))
            Button(onClick = {
                scene = when (scene) {
                    DemoScene.Text -> DemoScene.Icon
                    DemoScene.Icon -> DemoScene.Text
                }
            }) {
                Text("toggle")
            }
            Spacer(Modifier.height(16.dp))
            when (scene) {
                DemoScene.Text ->
                    Crossfade(
                        targetState = scene,
                        animationSpec = tween(durationMillis = 1000)
                    ) {
                        Text(text = "Phone", fontSize = 32.sp)
                    }
                DemoScene.Icon ->
                    Crossfade(
                        targetState = scene,
                        animationSpec = tween(durationMillis = 1000)
                    ) {
                        Icon(
                            imageVector = Icons.Default.Phone,
                            null,
                            modifier = Modifier.size(48.dp)
                        )
                    }
            }
        }
    }

    @OptIn(ExperimentalAnimationApi::class)
    @Composable
    fun AnimationContentDemo() {
        Row {
            var count by remember { mutableStateOf(0) }
            Button(onClick = { count++ }) {
                Text("Add")
            }
            AnimatedContent(
                targetState = count,
                transitionSpec = {
                    // Compare the incoming number with the previous number.
                    if (targetState > initialState) {
                        // If the target number is larger, it slides up and fades in
                        // while the initial (smaller) number slides up and fades out.
                        slideInVertically { height -> height } + fadeIn() with
                                slideOutVertically { height -> -height } + fadeOut()
                    } else {
                        // If the target number is smaller, it slides down and fades in
                        // while the initial number slides down and fades out.
                        slideInVertically { height -> -height } + fadeIn() with
                                slideOutVertically { height -> height } + fadeOut()
                    }.using(
                        // Disable clipping since the faded slide-in/out should
                        // be displayed out of bounds.
                        SizeTransform(clip = false)
                    )
                }
            ) { targetCount ->
                Spacer(modifier = Modifier.height(10.dp))
                Box(
                    modifier = Modifier
                        .padding(vertical = 2.dp, horizontal = 8.dp)
                        .background(Color.LightGray)
                        .size(200.dp),
                    contentAlignment = Alignment.Center
                ) {
                    Spacer(modifier = Modifier.height(10.dp))
                    Text(
                        text = "$targetCount",
                        color = MaterialTheme.colors.secondary,
                        style = MaterialTheme.typography.caption,
                        textAlign = TextAlign.Center
                    )
                }

            }
        }
    }
}

